hxndg:
trycrackme 的下载地址为 https://crackmes.one/crackme/61c8deff33c5d413767ca0ea ,直接从汇编就能看出来到底在做什么
Dump of assembler code for function main:
# 标准开局,保存堆栈
0x00005555555551af : push %rbp
0x00005555555551b0 : mov %rsp,%rbp
0x00005555555551b3 : sub $0xe0,%rsp
#存储 argc & argv ,不过这里没用到
0x00005555555551ba : mov %edi,-0xd4(%rbp)
0x00005555555551c0 : mov %rsi,-0xe0(%rbp)
0x00005555555551c7 : mov %fs:0x28,%rax
0x00005555555551d0 : mov %rax,-0x8(%rbp)
0x00005555555551d4 : xor %eax,%eax
# 注意这个常量,是我们比较的关键
0x00005555555551d6 : movabs $0x3534323773734034,%rax
# 存储到了-0xbb(%rbp)的位置
0x00005555555551e0 : mov %rax,-0xbb(%rbp)
# 给上面的常量补充了两个字节的数字,拼接到最后面,拼接完以后看一下具体的内容
# (gdb) x/16xb 140737488347509
# 0x7fffffffe175: 0x34 0x40 0x73 0x73 0x37 0x32 0x34 0x35
# 0x7fffffffe17d: 0x33 0x36 0x00 0x00 0x00 0x00 0x00 0x00
0x00005555555551e7 : movw $0x3633,-0xb3(%rbp)
0x00005555555551f0 : movb $0x0,-0xb1(%rbp)
0x00005555555551f7 : lea -0xbb(%rbp),%rax
0x00005555555551fe : mov %rax,%rdi
0x0000555555555201 : callq 0x555555555050
# 存储我们刚才拼接出来的字符串长度,明确地看出来是 10
0x0000555555555206 : mov %eax,-0xc0(%rbp)
0x000055555555520c : mov $0x0,%eax
# 调用 banner 打印一些 flag ,没有啥用,不用管
0x0000555555555211 : callq 0x555555555199
# 准备调用 printf 提示用户输入数据,格式化字符串为 Put the key:
0x0000555555555216 : lea 0xef9(%rip),%rax # 0x555555556116
0x000055555555521d : mov %rax,%rdi
0x0000555555555220 : mov $0x0,%eax
0x0000555555555225 : callq 0x555555555070
# 记住这个-0xb0(%rbp)的地址,这个是存储 scanf 输入进来的地址
0x000055555555522a : lea -0xb0(%rbp),%rax
0x0000555555555231 : mov %rax,%rsi
0x0000555555555234 : lea 0xee9(%rip),%rax # 0x555555556124
0x000055555555523b : mov %rax,%rdi
0x000055555555523e : mov $0x0,%eax
0x0000555555555243 : callq 0x555555555080
# 初始化两个变量,分别存储上面-0xbb(%rbp)字符串处理过的字符个数
# 和要算出来作为正确的 code 所处理过的字符个数
0x0000555555555248 : movl $0x0,-0xc8(%rbp)
0x0000555555555252 : movl $0x0,-0xc4(%rbp)
|--- 0x000055555555525c : jmp 0x5555555552ab
| # -0xc8(%rbp)是刚才那个常量字符串处理过的 byte 数,所以 cltq 下面那句就很明显了
|-----> 0x000055555555525e : mov -0xc8(%rbp),%eax
| | 0x0000555555555264 : cltq
| | # 这里的意思是把刚才-0xbb(%rbp)字符串的字符,hex 形式丢到 eax
| | 0x0000555555555266 : movzbl -0xbb(%rbp,%rax,1),%eax
| | # 先做有符号数拓展,再做无符号数拓展,不过都小于 0x80 ,所以无所谓了
| | 0x000055555555526e : movsbl %al,%eax
| | 0x0000555555555271 : movzbl %al,%eax
| | 0x0000555555555274 : mov -0xc4(%rbp),%edx
| | 0x000055555555527a : movslq %edx,%rdx
| | # rcx 存储了计算结果的首地址, -0x70(%rbp)是我们最后比较的参照物的地址
| | 0x000055555555527d : lea -0x70(%rbp),%rcx
| | # 加上已经结算过的结果,实际上就是挪动指针,存储下面 sprintf 的结果
| | 0x0000555555555281 : add %rdx,%rcx
| | 0x0000555555555284 : mov %eax,%edx
| | # 这个字符是 %02x
| | 0x0000555555555286 : lea 0xe9a(%rip),%rax # 0x555555556127
| | 0x000055555555528d : mov %rax,%rsi
| | 0x0000555555555290 : mov %rcx,%rdi
| | 0x0000555555555293 : mov $0x0,%eax
| | # 这里调用 sprintf 的含义就非常清楚了,从 hex 编码转换为字符串
| | # 原先是 hex 0x34 ,那么转换为字符串\"34\"
| | 0x0000555555555298 : callq 0x555555555090
| | # hex 字符串处理过一 byte 后挪一
| | # 而 sprintf 处理的结果是 2byte (两个 char 字符)
| | 0x000055555555529d : addl $0x1,-0xc8(%rbp)
| | 0x00005555555552a4 : addl $0x2,-0xc4(%rbp)
| | #开始处理,先找到第一个字符,看看和上面的字符串长度 10 的大小,判断有没有处理完
| ---> 0x00005555555552ab : mov -0xc8(%rbp),%eax
| 0x00005555555552b1 : cmp -0xc0(%rbp),%eax
|------ 0x00005555555552b7 : jl 0x55555555525e
0x00005555555552b9 : lea -0x70(%rbp),%rax
0x00005555555552bd : mov %rax,%rdi
0x00005555555552c0 : callq 0x555555555050
0x00005555555552c5 : mov %rax,%rdx
# 算出来的正确的 code
0x00005555555552c8 : lea -0x70(%rbp),%rcx
# 输入的 code
0x00005555555552cc : lea -0xb0(%rbp),%rax
0x00005555555552d3 : mov %rcx,%rsi
0x00005555555552d6 : mov %rax,%rdi
# 比较
0x00005555555552d9 : callq 0x555555555030
0x00005555555552de : test %eax,%eax
|----0x00005555555552e0 : je 0x5555555552fd
| 0x00005555555552e2 : lea 0xe43(%rip),%rax # 0x55555555612c
| 0x00005555555552e9 : mov %rax,%rdi
| 0x00005555555552ec : mov $0x0,%eax
| 0x00005555555552f1 : callq 0x555555555070
| 0x00005555555552f6 : mov $0xffffffff,%eax
| 0x00005555555552fb : jmp 0x555555555316
| # 这里就是正确的结果,所以我们只需要输入一个字符串和上面常量字符串从 hex 到字符串转换的结果即可
|--->0x00005555555552fd : lea 0xe3b(%rip),%rax # 0x55555555613f
0x0000555555555304 : mov %rax,%rdi
0x0000555555555307 : mov $0x0,%eax
0x000055555555530c : callq 0x555555555070
0x0000555555555311 : mov $0x0,%eax
0x0000555555555316 : mov -0x8(%rbp),%rdx
0x000055555555531a : sub %fs:0x28,%rdx
0x0000555555555323 : je 0x55555555532a
0x0000555555555325 : callq 0x555555555060
0x000055555555532a : leaveq
0x000055555555532b : retq
End of assembler dump.
简单说一下过程
先从常量拼接出来要产生的字符串,0x3534323773734034 拼接 0x3633
对每一 byte 调用 sprintf ,使用格式:%02x ,从 hex 转换为两 byte 的字符串
转换完的结果和我们输入的结果做对比,一致则成功
|